﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Xml.Linq;
using System.IO;
using System.Xml.XPath;
using System.Xml;
using TST.DemoCode.Domain;
using TST.DemoCode.EventReceivers;

namespace TST.DemoCode.FeatureReceivers
{
    public class FoldersToFieldsSetup : TSTBaseFeatureReceiver
    {
        private const String XmlNameSpace = "http://schemas.tonstegeman.com/events/2010/03";

        public FoldersToFieldsSetup() : base()
        {
        }

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            _properties = properties;
            try
            {
                if (_properties.Feature.Parent is SPSite)
                {
                    using (SPSite site = properties.Feature.Parent as SPSite)
                    {
                        if (site == null)
                            return;
                        using (SPWeb web = site.RootWeb)
                        {
                            RegisterEventReceivers(web, true);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex);
                throw ex;
            }
        }

        private void StoreFieldsSetup(SPWeb web, SPContentType contentType, XElement node)
        {
            StringBuilder setup = new StringBuilder();
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
            nsmgr.AddNamespace("ftf", XmlNameSpace);
            IEnumerable<XElement> fields = node.XPathSelectElements("ftf:Fields/ftf:Field", nsmgr);
            foreach (XElement fieldsNode in fields)
            {
                String field = XmlHelper.GetAttributeValue(fieldsNode, "Name");
                if (!String.IsNullOrEmpty(field))
                {
                    setup.AppendFormat("{0};", field);
                }
            }
            String key = FoldersToFields.GetSetupPropertyKey(contentType);
            web.Properties[key] = setup.ToString();
            web.AllProperties[key] = setup.ToString();
            web.Update();
            web.Properties.Update();
        }

        private void RemoveFieldsSetup(SPWeb web, SPContentType contentType)
        {
            String key = FoldersToFields.GetSetupPropertyKey(contentType);
            if (web.AllProperties.ContainsKey(key))
            {
                web.AllProperties.Remove(key);
                web.Properties[key] = null;
                web.Update();
                web.Properties.Update();
            }
        }

        private void LogInfo(String message)
        {
            System.Diagnostics.Trace.TraceInformation(message);
        }

        private void RegisterEventReceivers(SPWeb web, Boolean register)
        {
            LogInfo(String.Format("Start RegisterEventReceivers for web {0} ({1}).", web.Url, register));
            String fileName = GetSettingFromProperties("FileName", true);
            String fileDir = _properties.Definition.RootDirectory;
            if (!fileDir.EndsWith(@"\"))
            {
                fileDir += @"\";
            }
            fileName = fileDir + fileName;
            LogVerbose(String.Format("Load Xml File {0}.", fileName));
            if (!File.Exists(fileName))
            {
                throw new SPException(String.Format("File not found: {0}.", fileName));
            }
            XDocument xmlFile = XDocument.Load(fileName);
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
            nsmgr.AddNamespace("ftf", XmlNameSpace);

            LogVerbose("Select ContentType Nodes");
            IEnumerable<XElement> contentTypes = xmlFile.XPathSelectElements("ftf:FoldersToFieldsSettings/ftf:ContentTypes/ftf:ContentType", nsmgr);
            foreach (XElement node in contentTypes)
            {
                // Get the content type.
                String name = XmlHelper.GetAttributeValue(node, "Name");
                if (String.IsNullOrEmpty(name))
                {
                    LogInfo("ContentType Node without a 'Name 'attribute'");
                    continue;
                }
                LogVerbose(String.Format("Search Content type {0}.", name));
                SPContentType contentType = null;
                foreach (SPContentType search in web.ContentTypes)
                {
                    if (string.Compare(search.Name, name, true) == 0)
                    {
                        contentType = search;
                        break;
                    }
                }
                if (contentType == null)
                    throw new SPException(string.Format("ContentType {0} not found in site at url {1}.", name, web.Url));

                Boolean modified = false;
                if (register)
                {
                    LogVerbose("Register event handlers");
                    modified = modified | RegisterEventreceiver(contentType, SPEventReceiverType.ItemAdded, 101);
                    modified = modified | RegisterEventreceiver(contentType, SPEventReceiverType.ItemUpdated, 102);
                    modified = modified | RegisterEventreceiver(contentType, SPEventReceiverType.ItemFileMoved, 103);

                    LogVerbose("Store fields setup in ParentWeb property bag.");
                    StoreFieldsSetup(web, contentType, node);
                }
                else
                {
                    LogVerbose("Unregister event handlers");
                    modified = UnregisterEventreceivers(contentType);

                    LogVerbose("Remove fields setup from ParentWeb property bag.");
                    RemoveFieldsSetup(web, contentType);
                }
                if (modified)
                {
                    LogVerbose("Update content type.");
                    contentType.Update(true);
                }
            }
            LogVerbose("RegisterEventReceivers finished.");
            return;
        }

        private Boolean UnregisterEventreceivers(SPContentType contentType)
        {
            Boolean result = false;
            SPEventReceiverDefinition receiverDefinition = null;
            LogVerbose(String.Format("UnregisterEventreceivers: {0} found.", contentType.EventReceivers.Count));
            for (int i = contentType.EventReceivers.Count - 1; i >= 0; i--)
            {
                receiverDefinition = contentType.EventReceivers[i];
                if (String.Compare(receiverDefinition.Class, typeof(FoldersToFields).FullName, true) == 0)
                {
                    LogInfo(String.Format("Unregister Eventreceiver: {0} ({1}).", receiverDefinition.Name, receiverDefinition.Type));
                    receiverDefinition.Delete();
                    result = true;
                }
            }
            return result;
        }

        private bool RegisterEventreceiver(SPContentType contentType, SPEventReceiverType eventReceiverType, int sequenceNumber)
        {
            //Associate the receiver, if it is not yet associated.
            Boolean found = false;
            LogVerbose(String.Format("RegisterEventreceiver: find {0}.", eventReceiverType));
            foreach (SPEventReceiverDefinition def in contentType.EventReceivers)
            {
                if (String.Compare(def.Class, typeof(FoldersToFields).FullName, true) == 0 &&
                    def.Type == eventReceiverType)
                {
                    found = true;
                    break;
                }
            }

            if (!found)
            {
                LogInfo(String.Format("Eventreceiver {0} not found. Add.", eventReceiverType));
                SPEventReceiverDefinition receiver = contentType.EventReceivers.Add();
                receiver.Name = String.Format("EventFoldersToFields_{0}", eventReceiverType.ToString());
                receiver.Type = eventReceiverType;
                receiver.SequenceNumber = sequenceNumber;
                receiver.Assembly = typeof(FoldersToFields).Assembly.FullName;
                receiver.Class = typeof(FoldersToFields).FullName;
                LogVerbose("Update new event handler");
                receiver.Update();
                return true;
            }
            else
            {
                LogVerbose(String.Format("Eventreceiver {0} found. Ignore.", eventReceiverType));
            }
            return false;
        }

        private void LogVerbose(string message)
        {
            System.Diagnostics.Trace.TraceInformation(message);
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            _properties = properties;
            try
            {
                if (_properties.Feature.Parent is SPSite)
                {
                    using (SPSite site = _properties.Feature.Parent as SPSite)
                    {
                        if (site == null)
                            return;
                        using (SPWeb web = site.RootWeb)
                        {
                            RegisterEventReceivers(web, false);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError(ex);
                throw ex;
            }
        }

        private void LogError(Exception ex)
        {
            System.Diagnostics.Trace.TraceError(String.Format("Error: {0}.", ex.Message));
        }

        public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {
        }

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {
        }
    }
}
